# -*- coding: utf-8 -*-
"""
自动财务处理服务

负责处理预约相关的自动扣费和退款功能
包括余额验证、自动扣费、退款处理和交易记录生成
"""

from datetime import datetime, timedelta
from decimal import Decimal
from typing import Optional, Dict, Any, Tuple
from flask import current_app
from sqlalchemy.exc import SQLAlchemyError

from app import db
from app.controllers.account_controller import AccountController
from app.models.account import UserAccount, Transaction
from app.models.reservation import Reservation
from app.models.user import User
from app.utils.exceptions import BusinessError, ValidationError


class AutoFinanceService:
    """自动财务处理服务"""

    @staticmethod
    def validate_balance_for_reservation(user_id: int, amount: Decimal) -> Dict[str, Any]:
        """
        验证用户余额是否足够支付预约费用

        Args:
            user_id: 用户ID
            amount: 预约费用

        Returns:
            验证结果信息

        Raises:
            BusinessError: 余额不足或账户异常
        """
        try:
            # 获取账户信息
            account = UserAccount.query.filter_by(user_id=user_id).first()
            if not account:
                return {
                    'success': False,
                    'error': '账户未开通',
                    'error_code': 'ACCOUNT_NOT_EXISTS'
                }


            # 检查余额是否充足
            if account.balance < amount:
                return {
                    'success': False,
                    'error': f'余额不足，当前余额: {account.balance}元，需要: {amount}元',
                    'error_code': 'INSUFFICIENT_BALANCE',
                    'current_balance': float(account.balance),
                    'available_balance': float(account.balance),
                    'required_amount': float(amount),
                    'shortage': float(amount - account.balance)
                }

            return {
                'success': True,
                'current_balance': float(account.balance),
                'available_balance': float(account.balance),
                'required_amount': float(amount),
                'remaining_balance': float(account.balance - amount)
            }

        except SQLAlchemyError as e:
            current_app.logger.error(f"验证余额失败 - 用户ID: {user_id}, 错误: {str(e)}")
            raise BusinessError("系统错误，请稍后重试")

    @staticmethod
    def process_reservation_payment(reservation_id: int) -> Dict[str, Any]:
        """
        处理预约确认时的自动扣费

        Args:
            reservation_id: 预约ID

        Returns:
            扣费结果信息

        Raises:
            BusinessError: 扣费失败
        """
        try:
            # 获取预约信息
            reservation = Reservation.query.get(reservation_id)
            if not reservation:
                raise BusinessError("预约记录不存在")

            # 检查预约状态
            if reservation.payment_status == 'paid':
                return {
                    'success': True,
                    'message': '该预约已支付',
                    'transaction_id': None,
                    'already_paid': True
                }

            if reservation.status not in ['confirmed', 'pending']:
                raise BusinessError("无效的预约状态，无法扣费")

            # 验证余额
            validation_result = AutoFinanceService.validate_balance_for_reservation(
                reservation.student_id, reservation.price
            )

            if not validation_result['success']:
                # 余额不足，更新预约状态
                reservation.payment_status = 'failed'
                reservation.payment_failure_reason = validation_result['error']
                db.session.commit()

                return {
                    'success': False,
                    'error': validation_result['error'],
                    'error_code': validation_result['error_code'],
                    'validation_details': validation_result
                }

            # 执行扣费
            description = f"预约扣费 - {reservation.reservation_date.strftime('%Y-%m-%d')} " \
                         f"{reservation.start_time.strftime('%H:%M')}-{reservation.end_time.strftime('%H:%M')}"

            if reservation.coach:
                description += f" (教练: {reservation.coach.real_name or reservation.coach.username})"

            if reservation.table:
                description += f" (球台: {reservation.table.name})"

            # 添加更多元数据
            metadata = {
                'reservation_id': reservation.id,
                'student_id': reservation.student_id,
                'coach_id': reservation.coach_id,
                'table_id': reservation.table_id,
                'reservation_date': reservation.reservation_date.strftime('%Y-%m-%d'),
                'start_time': reservation.start_time.strftime('%H:%M'),
                'end_time': reservation.end_time.strftime('%H:%M'),
                'duration_minutes': AutoFinanceService._calculate_duration_minutes(
                    reservation.start_time, reservation.end_time
                ),
                'auto_payment': True,
                'payment_method': 'balance_deduction'
            }

            payment_result = AccountController.consume_balance(
                user_id=reservation.student_id,
                amount=reservation.price,
                description=description,
                reference_type='reservation',
                reference_id=reservation.id,
                metadata=metadata
            )

            if payment_result['success']:
                # 更新预约支付状态
                reservation.payment_status = 'paid'
                reservation.payment_time = datetime.now()
                reservation.transaction_id = payment_result['transaction_id']

                # 如果预约状态是pending，更新为confirmed
                if reservation.status == 'pending':
                    reservation.status = 'confirmed'

                db.session.commit()

                current_app.logger.info(
                    f"预约自动扣费成功 - 预约ID: {reservation_id}, "
                    f"用户ID: {reservation.student_id}, 金额: {reservation.price}, "
                    f"交易ID: {payment_result['transaction_id']}"
                )

                return {
                    'success': True,
                    'transaction_id': payment_result['transaction_id'],
                    'amount': float(reservation.price),
                    'new_balance': payment_result['new_balance'],
                    'payment_time': reservation.payment_time,
                    'message': '预约扣费成功'
                }
            else:
                # 扣费失败，更新预约状态
                reservation.payment_status = 'failed'
                reservation.payment_failure_reason = '扣费失败'
                db.session.commit()

                return {
                    'success': False,
                    'error': '扣费失败',
                    'error_code': 'PAYMENT_FAILED'
                }

        except BusinessError:
            raise
        except SQLAlchemyError as e:
            db.session.rollback()
            current_app.logger.error(f"预约扣费失败 - 预约ID: {reservation_id}, 错误: {str(e)}")
            raise BusinessError("扣费失败，请稍后重试")

    @staticmethod
    def process_reservation_refund(reservation_id: int, refund_reason: str = None) -> Dict[str, Any]:
        """
        处理预约取消时的自动退款

        Args:
            reservation_id: 预约ID
            refund_reason: 退款原因

        Returns:
            退款结果信息

        Raises:
            BusinessError: 退款失败
        """
        try:
            # 获取预约信息
            reservation = Reservation.query.get(reservation_id)
            if not reservation:
                raise BusinessError("预约记录不存在")

            # 检查是否需要退款
            if reservation.payment_status != 'paid':
                return {
                    'success': True,
                    'message': '该预约未支付，无需退款',
                    'refund_amount': 0,
                    'no_refund_needed': True
                }

            # 计算退款金额（考虑取消政策）
            refund_calculation = AutoFinanceService._calculate_refund_amount(reservation)

            if refund_calculation['refund_amount'] <= 0:
                # 无退款
                reservation.payment_status = 'no_refund'
                reservation.refund_reason = refund_calculation['reason']
                db.session.commit()

                return {
                    'success': True,
                    'refund_amount': 0,
                    'penalty_amount': float(refund_calculation['penalty_amount']),
                    'message': refund_calculation['reason'],
                    'no_refund_due_to_policy': True
                }

            # 获取原交易记录
            original_transaction = Transaction.query.filter_by(
                reference_type='reservation',
                reference_id=reservation.id,
                type='reservation'
            ).first()

            if not original_transaction:
                raise BusinessError("找不到原交易记录")

            # 执行退款
            refund_description = f"预约退款 - {reservation.reservation_date.strftime('%Y-%m-%d')} " \
                               f"{reservation.start_time.strftime('%H:%M')}-{reservation.end_time.strftime('%H:%M')}"

            if refund_reason:
                refund_description += f" (原因: {refund_reason})"

            if refund_calculation['penalty_amount'] > 0:
                refund_description += f" (违约金: {refund_calculation['penalty_amount']}元)"

            # 添加退款元数据
            refund_metadata = {
                'original_transaction_id': original_transaction.id,
                'original_amount': float(reservation.price),
                'refund_amount': float(refund_calculation['refund_amount']),
                'penalty_amount': float(refund_calculation['penalty_amount']),
                'hours_until_reservation': refund_calculation['hours_until_reservation'],
                'refund_policy': refund_calculation['reason'],
                'reservation_id': reservation.id,
                'student_id': reservation.student_id,
                'auto_refund': True,
                'refund_method': 'balance_return'
            }

            refund_result = AccountController.refund_transaction(
                original_transaction.id,
                refund_description,
                metadata=refund_metadata
            )

            if refund_result['success']:
                # 如果有违约金，需要额外扣除
                if refund_calculation['penalty_amount'] > 0:
                    penalty_result = AccountController.consume_balance(
                        user_id=reservation.student_id,
                        amount=refund_calculation['penalty_amount'],
                        description=f"预约违约金 - {reservation.reservation_date.strftime('%Y-%m-%d')}",
                        reference_type='penalty',
                        reference_id=reservation.id
                    )

                    if not penalty_result['success']:
                        current_app.logger.warning(
                            f"违约金扣除失败 - 预约ID: {reservation_id}, "
                            f"金额: {refund_calculation['penalty_amount']}"
                        )

                # 更新预约状态
                reservation.payment_status = 'refunded'
                reservation.refund_time = datetime.now()
                reservation.refund_amount = refund_calculation['refund_amount']
                reservation.penalty_amount = refund_calculation['penalty_amount']
                reservation.refund_reason = refund_reason or '预约取消'

                db.session.commit()

                current_app.logger.info(
                    f"预约自动退款成功 - 预约ID: {reservation_id}, "
                    f"用户ID: {reservation.student_id}, "
                    f"退款金额: {refund_calculation['refund_amount']}, "
                    f"违约金: {refund_calculation['penalty_amount']}"
                )

                return {
                    'success': True,
                    'refund_amount': float(refund_calculation['refund_amount']),
                    'penalty_amount': float(refund_calculation['penalty_amount']),
                    'net_refund': float(refund_calculation['refund_amount'] - refund_calculation['penalty_amount']),
                    'refund_time': reservation.refund_time,
                    'message': '退款成功'
                }
            else:
                return {
                    'success': False,
                    'error': '退款失败',
                    'error_code': 'REFUND_FAILED'
                }

        except BusinessError:
            raise
        except SQLAlchemyError as e:
            db.session.rollback()
            current_app.logger.error(f"预约退款失败 - 预约ID: {reservation_id}, 错误: {str(e)}")
            raise BusinessError("退款失败，请稍后重试")

    @staticmethod
    def _calculate_refund_amount(reservation: Reservation) -> Dict[str, Any]:
        """
        计算退款金额（考虑取消政策）

        Args:
            reservation: 预约对象

        Returns:
            退款计算结果
        """
        now = datetime.now()
        reservation_datetime = datetime.combine(
            reservation.reservation_date,
            reservation.start_time
        )

        # 计算距离预约时间的小时数
        hours_until_reservation = (reservation_datetime - now).total_seconds() / 3600

        original_amount = reservation.price
        penalty_amount = Decimal('0')
        refund_amount = original_amount

        # 取消政策：
        # - 24小时以上：全额退款
        # - 2-24小时：收取20%违约金
        # - 2小时以内：不予退款
        if hours_until_reservation >= 24:
            # 24小时以上，全额退款
            penalty_amount = Decimal('0')
            refund_amount = original_amount
            reason = "提前24小时取消，全额退款"
        elif hours_until_reservation >= 2:
            # 2-24小时，收取20%违约金
            penalty_amount = original_amount * Decimal('0.2')
            refund_amount = original_amount - penalty_amount
            reason = f"提前{hours_until_reservation:.1f}小时取消，收取20%违约金"
        else:
            # 2小时以内，不予退款
            penalty_amount = original_amount
            refund_amount = Decimal('0')
            reason = f"距离预约时间不足2小时，不予退款"

        return {
            'original_amount': original_amount,
            'penalty_amount': penalty_amount,
            'refund_amount': refund_amount,
            'hours_until_reservation': hours_until_reservation,
            'reason': reason
        }

    @staticmethod
    def freeze_balance_for_reservation(user_id: int, amount: Decimal, reservation_id: int) -> bool:
        """
        为预约冻结用户余额

        Args:
            user_id: 用户ID
            amount: 冻结金额
            reservation_id: 预约ID

        Returns:
            是否冻结成功
        """
        try:
            reason = f"预约冻结 - 预约ID: {reservation_id}"
            # 数据库中无frozen_balance字段，不支持冻结功能
            current_app.logger.warning(f"冻结功能未实现 - 用户ID: {user_id}, 金额: {amount}")
            return True
        except Exception as e:
            current_app.logger.error(f"冻结余额失败 - 用户ID: {user_id}, 错误: {str(e)}")
            return False

    @staticmethod
    def unfreeze_balance_for_reservation(user_id: int, amount: Decimal, reservation_id: int) -> bool:
        """
        为预约解冻用户余额

        Args:
            user_id: 用户ID
            amount: 解冻金额
            reservation_id: 预约ID

        Returns:
            是否解冻成功
        """
        try:
            reason = f"预约解冻 - 预约ID: {reservation_id}"
            # 数据库中无frozen_balance字段，不支持解冻功能
            current_app.logger.warning(f"解冻功能未实现 - 用户ID: {user_id}, 金额: {amount}")
            return True
        except Exception as e:
            current_app.logger.error(f"解冻余额失败 - 用户ID: {user_id}, 错误: {str(e)}")
            return False

    @staticmethod
    def get_reservation_financial_summary(reservation_id: int) -> Dict[str, Any]:
        """
        获取预约的财务摘要信息

        Args:
            reservation_id: 预约ID

        Returns:
            财务摘要信息
        """
        try:
            reservation = Reservation.query.get(reservation_id)
            if not reservation:
                return {'error': '预约记录不存在'}

            # 获取相关交易记录
            transactions = Transaction.query.filter_by(
                reference_type='reservation',
                reference_id=reservation_id
            ).all()

            penalty_transactions = Transaction.query.filter_by(
                reference_type='penalty',
                reference_id=reservation_id
            ).all()

            refund_transactions = Transaction.query.filter_by(
                reference_type='refund'
            ).filter(
                Transaction.description.contains(f"预约ID: {reservation_id}")
            ).all()

            summary = {
                'reservation_id': reservation_id,
                'original_amount': float(reservation.price),
                'payment_status': reservation.payment_status,
                'payment_time': reservation.payment_time.isoformat() if reservation.payment_time else None,
                'refund_time': reservation.refund_time.isoformat() if reservation.refund_time else None,
                'refund_amount': float(reservation.refund_amount) if reservation.refund_amount else 0,
                'penalty_amount': float(reservation.penalty_amount) if reservation.penalty_amount else 0,
                'transactions': []
            }

            # 添加交易记录
            for transaction in transactions + penalty_transactions + refund_transactions:
                summary['transactions'].append({
                    'id': transaction.id,
                    'type': transaction.type,
                    'amount': float(transaction.amount),
                    'description': transaction.description,
                    'created_at': transaction.created_at.isoformat(),
                    'status': transaction.status
                })

            return summary

        except Exception as e:
            current_app.logger.error(f"获取预约财务摘要失败 - 预约ID: {reservation_id}, 错误: {str(e)}")
            return {'error': '获取财务摘要失败'}

    @staticmethod
    def batch_process_pending_payments(limit: int = 100) -> Dict[str, Any]:
        """
        批量处理待支付的预约

        Args:
            limit: 处理数量限制

        Returns:
            批量处理结果
        """
        try:
            # 查找待支付的预约
            pending_reservations = Reservation.query.filter_by(
                status='confirmed',
                payment_status='pending'
            ).limit(limit).all()

            results = {
                'total_processed': 0,
                'successful_payments': 0,
                'failed_payments': 0,
                'errors': []
            }

            for reservation in pending_reservations:
                try:
                    payment_result = AutoFinanceService.process_reservation_payment(reservation.id)
                    results['total_processed'] += 1

                    if payment_result['success']:
                        results['successful_payments'] += 1
                    else:
                        results['failed_payments'] += 1
                        results['errors'].append({
                            'reservation_id': reservation.id,
                            'error': payment_result.get('error', '未知错误')
                        })

                except Exception as e:
                    results['total_processed'] += 1
                    results['failed_payments'] += 1
                    results['errors'].append({
                        'reservation_id': reservation.id,
                        'error': str(e)
                    })

            current_app.logger.info(
                f"批量处理待支付预约完成 - 总数: {results['total_processed']}, "
                f"成功: {results['successful_payments']}, 失败: {results['failed_payments']}"
            )

            return results

        except Exception as e:
            current_app.logger.error(f"批量处理待支付预约失败: {str(e)}")
            return {
                'total_processed': 0,
                'successful_payments': 0,
                'failed_payments': 0,
                'errors': [{'error': str(e)}]
            }

    @staticmethod
    def get_user_payment_statistics(user_id: int, days: int = 30) -> Dict[str, Any]:
        """
        获取用户支付统计信息

        Args:
            user_id: 用户ID
            days: 统计天数

        Returns:
            支付统计信息
        """
        try:
            start_date = datetime.now() - timedelta(days=days)

            # 获取用户的预约相关交易
            account = UserAccount.query.filter_by(user_id=user_id).first()
            if not account:
                return {'error': '账户不存在'}

            transactions = Transaction.query.filter(
                Transaction.user_id == user_id,
                Transaction.created_at >= start_date,
                Transaction.reference_type.in_(['reservation', 'penalty', 'refund'])
            ).all()

            stats = {
                'total_paid': 0,
                'total_refunded': 0,
                'total_penalty': 0,
                'transaction_count': len(transactions),
                'successful_reservations': 0,
                'cancelled_reservations': 0,
                'penalty_rate': 0
            }

            for transaction in transactions:
                if transaction.type == 'reservation':
                    stats['total_paid'] += abs(float(transaction.amount))
                    stats['successful_reservations'] += 1
                elif transaction.type == 'refund':
                    stats['total_refunded'] += float(transaction.amount)
                    stats['cancelled_reservations'] += 1
                elif transaction.type == 'consume':
                    stats['total_penalty'] += abs(float(transaction.amount))

            # 计算违约率
            if stats['successful_reservations'] + stats['cancelled_reservations'] > 0:
                stats['penalty_rate'] = (
                    stats['cancelled_reservations'] /
                    (stats['successful_reservations'] + stats['cancelled_reservations']) * 100
                )

            return stats

        except Exception as e:
            current_app.logger.error(f"获取用户支付统计失败 - 用户ID: {user_id}, 错误: {str(e)}")
            return {'error': '获取支付统计失败'}

    @staticmethod
    def _calculate_duration_minutes(start_time, end_time):
        """
        计算时间段的分钟数

        Args:
            start_time: 开始时间 (time对象)
            end_time: 结束时间 (time对象)

        Returns:
            int: 时长（分钟）
        """
        start_minutes = start_time.hour * 60 + start_time.minute
        end_minutes = end_time.hour * 60 + end_time.minute
        return end_minutes - start_minutes

    @staticmethod
    def generate_transaction_report(start_date, end_date, transaction_types=None):
        """
        生成交易报告

        Args:
            start_date: 开始日期
            end_date: 结束日期
            transaction_types: 交易类型过滤列表

        Returns:
            Dict: 交易报告数据
        """
        try:
            from datetime import datetime

            # 构建查询条件
            query = Transaction.query.filter(
                Transaction.created_at >= start_date,
                Transaction.created_at <= end_date
            )

            if transaction_types:
                query = query.filter(Transaction.type.in_(transaction_types))

            transactions = query.all()

            report = {
                'period': {
                    'start_date': start_date.isoformat(),
                    'end_date': end_date.isoformat()
                },
                'summary': {
                    'total_transactions': len(transactions),
                    'total_amount': 0,
                    'types': {}
                },
                'details': []
            }

            type_summary = {}
            total_amount = 0

            for transaction in transactions:
                transaction_data = {
                    'id': transaction.id,
                    'user_id': transaction.user_id,
                    'type': transaction.type,
                    'amount': float(transaction.amount),
                    'description': transaction.description,
                    'created_at': transaction.created_at.isoformat(),
                    'reference_type': transaction.reference_type,
                    'reference_id': transaction.reference_id,
                    'status': transaction.status
                }

                # 如果有元数据，解析并添加
                if hasattr(transaction, 'metadata') and transaction.metadata:
                    try:
                        import json
                        transaction_data['metadata'] = json.loads(transaction.metadata)
                    except:
                        transaction_data['metadata'] = transaction.metadata

                report['details'].append(transaction_data)

                # 统计汇总
                trans_type = transaction.type
                if trans_type not in type_summary:
                    type_summary[trans_type] = {
                        'count': 0,
                        'total_amount': 0
                    }

                type_summary[trans_type]['count'] += 1
                type_summary[trans_type]['total_amount'] += float(transaction.amount)
                total_amount += float(transaction.amount)

            report['summary']['total_amount'] = total_amount
            report['summary']['types'] = type_summary

            current_app.logger.info(
                f"生成交易报告完成 - 期间: {start_date} 到 {end_date}, "
                f"交易数量: {len(transactions)}, 总金额: {total_amount}"
            )

            return report

        except Exception as e:
            current_app.logger.error(f"生成交易报告失败: {str(e)}")
            return {'error': '生成交易报告失败'}